Three.js
虽然内置了一些模型,但在实际业务中往往需要导入外部模型,导入的模型可以视为自定义模型。
首先要知道的是模型本质上来说是「三维坐标的集合」,最简单的一个模型可以用三个坐标和连接这三个坐标的「面」来表示。
模型就是数字
1 | const vertices = [ |
这段代码表示有三个坐标分别为 0, 0,0
、0, 100, 0
和 100, 0, 0
,并且这三个点在 vertices
数组中的下标依次为 0
、1
、2
,依次连接我们就可以得到一个三角形「面」,所以 [0, 1, 2]
表示的就是这个三角形的面。
当然现在只有三个点所以很简单,但如果再多一个点,哪几个点连接才能变成我们期望的那个形状呢,所以 faces
的意义就在这里,模型有几个面,这个面是由哪三个点连接而成的。
1 | const vertices = [ |
所以看到的三角形是
并且,直角在右上角的面可以用 [1, 3, 2]
表示;直角在左上角的可以用 [0, 1, 3]
表示;直角在右下角的可以用 [0, 2, 3]
表示;
可能在这里有疑问,这不是一个平面吗,不是模型呀,模型应该是立体的。
其实模型就是多个面组合在一起,又因为面其实就是点,所以开始才说「模型就是三维坐标的集合」。如果想生成一个普遍意义上的模型,可以再增加 1 个三维坐标,和已有的三个坐标组成四个面。
1 | const vertices = [ |
这是我理解的模型的本质。
手写 obj 文件渲染模型
再以 obj
格式的模型文件来说明,我们可以手写一个 obj
文件导入到任意支持 obj
格式的软件中,也能渲染出我们预期的模型,仍然拿上面的例子说明。
使用记事本创建一个文本,写入下面内容后将后缀保存为 example.obj
1 | o Mesh |
第一行 o Mesh
表示开始一个模型,名字为 Mesh
;第二至第五行 v
开头表示四个三维坐标;第七至十行 f
开头表示四个面,这里需要注意的是下标不是从 0 开始,而是从 1 开始。
使用 blender
导入该文件,能正确渲染我们预期的模型。
给自定义模型贴图
贴图的本质就是把面上的点对应到贴图上,现在有这样一张贴图
需要把这三个三角形贴到之前的模型的三个等边直角三角形上,只要修改 geometry.faceVertexUvs[0]
即可,对应的值是每个面顶点在贴图上的相对位置。有点难描述,geometry
第一个面是 0, 1, 2
,我们想给这个面贴上绿色的这个三角形,其实就是把 0, 1, 2
这三个点「放到」贴图上
1 | geometry.faceVertexUvs[0] = [ |
这里还是用 blender
来展示效果,当 blender
加载 example.obj
文件时,会加载相同目录下的同名 mtl
文件,所以再创建一个 example.mtl
文件
1 | newmtl material |
最后一行 map_Kd texture.png
指定了贴图文件路径,可以使用网络地址和绝对路径;以及修改 example.obj
文件,指定「相对位置」和面上的点和相对位置的对应关系。
1 | # 这行必须,指定贴图文件 |
将 example.obj
、example.mtl
和 texture.png
放在同一目录下,使用 blender
重新导入 example.obj
,点击上方 Shading
菜单,即可看到贴图后的效果。
这是后下方的视角。
要给其他面贴图同理。
总结
虽然上面的例子很简单,但理解了这个例子,再复杂的模型也是一样的原理。并且知道了模型的本质,也能帮助理解模型的变换,甚至可以实现 three.js
不提供的更复杂的功能。
刚开始使用 three.js
时,three.js就像一个黑盒一样,输入一个
obj` 文件,输出一个渲染好的模型,这中间发生了什么一直不清楚,遇到问题也不知道怎么解决,甚至于定位问题都做不到,不过后来慢慢摸索,总算是理解了一部分,材质那部分到现在还不是很理解,需要再花些时间去探索。